"""
TTS语音合成模块
负责将文本转换为语音,并提取口型同步数据
"""

import os
import time
import wave
import tempfile
from typing import List, Optional, Tuple
from io import BytesIO
from pathlib import Path

from aip import AipSpeech
import numpy as np
from pydub import AudioSegment

from ..core.models import LipSyncData
from ..utils.logger import get_logger
from ..utils.temp_file_manager import get_temp_file_manager


class TTSEngine:
    """百度TTS语音合成引擎"""

    def __init__(
        self,
        app_id: str,
        api_key: str,
        secret_key: str,
        per: int = 4,  # 发音人
        spd: int = 5,  # 语速
        pit: int = 5,  # 音调
        vol: int = 8,  # 音量
        aue: int = 6   # 音频格式(6=WAV)
    ):
        """
        初始化TTS引擎
        
        Args:
            app_id: 百度应用ID
            api_key: 百度API Key
            secret_key: 百度Secret Key
            per: 发音人(0-度小美,1-度小宇,3-度逍遥,4-度丫丫)
            spd: 语速(0-15)
            pit: 音调(0-15)
            vol: 音量(0-15)
            aue: 音频格式(3-MP3,6-WAV)
        """
        self.app_id = app_id
        self.api_key = api_key
        self.secret_key = secret_key
        self.per = per
        self.spd = spd
        self.pit = pit
        self.vol = vol
        self.aue = aue

        # 初始化百度语音客户端
        self.client = AipSpeech(app_id, api_key, secret_key)

        self.logger = get_logger()
        self.current_audio: Optional[AudioSegment] = None
        self.current_lipsync_data: List[LipSyncData] = []
        
        # 使用统一的临时文件管理器
        self.temp_manager = get_temp_file_manager()
        self.temp_dir = self.temp_manager.get_temp_dir('audio')
        self.logger.debug(f"TTS临时文件目录: {self.temp_dir}")

    def synthesize(self, text: str) -> Tuple[Optional[AudioSegment], List[LipSyncData]]:
        """
        合成语音
        
        Args:
            text: 要合成的文本
            
        Returns:
            (音频对象, 口型同步数据列表)
        """
        try:
            self.logger.debug(f"正在合成语音: {text[:50]}...")

            # 调用百度TTS API
            result = self.client.synthesis(
                text,
                'zh',
                1,
                {
                    'per': self.per,
                    'spd': self.spd,
                    'pit': self.pit,
                    'vol': self.vol,
                    'aue': self.aue
                }
            )

            # 检查是否返回错误
            if isinstance(result, dict):
                error_msg = result.get('err_msg', '未知错误')
                self.logger.error(f"TTS合成失败: {error_msg}")
                return None, []

            # 保存音频到项目临时目录
            import uuid
            temp_filename = f"tts_{uuid.uuid4().hex[:8]}.wav"
            temp_file_path = self.temp_dir / temp_filename
            
            # 跟踪临时文件
            self.temp_manager.track_file(temp_file_path)
            
            with open(temp_file_path, 'wb') as f:
                f.write(result)

            # 加载音频
            audio = AudioSegment.from_file(str(temp_file_path), format='wav')
            self.current_audio = audio

            # 提取口型同步数据
            lipsync_data = self._extract_lipsync_data(str(temp_file_path))
            self.current_lipsync_data = lipsync_data

            # 删除临时文件
            try:
                self.temp_manager.cleanup_file(temp_file_path)
            except Exception as e:
                self.logger.warning(f"清理临时文件失败: {e}")

            self.logger.info(f"✓ 语音合成成功,时长: {len(audio)/1000:.2f}秒")

            return audio, lipsync_data

        except Exception as e:
            self.logger.error(f"语音合成异常: {e}")
            return None, []

    def _extract_lipsync_data(self, audio_file: str) -> List[LipSyncData]:
        """
        从音频文件提取口型同步数据
        
        Args:
            audio_file: 音频文件路径
            
        Returns:
            口型同步数据列表
        """
        try:
            # 打开WAV文件
            with wave.open(audio_file, 'rb') as wav_file:
                # 获取音频参数
                sample_rate = wav_file.getframerate()
                n_frames = wav_file.getnframes()
                audio_data = wav_file.readframes(n_frames)

                # 转换为numpy数组
                audio_array = np.frombuffer(audio_data, dtype=np.int16)

                # 计算每帧的时间间隔(60 FPS)
                fps = 60
                frame_interval = 1.0 / fps
                samples_per_frame = int(sample_rate * frame_interval)

                lipsync_data = []

                # 按帧分析音频
                for i in range(0, len(audio_array), samples_per_frame):
                    frame_data = audio_array[i:i + samples_per_frame]

                    if len(frame_data) == 0:
                        break

                    # 计算RMS音量
                    rms = np.sqrt(np.mean(frame_data.astype(np.float32) ** 2))
                    max_amplitude = 32768.0  # 16-bit音频最大值
                    volume = min(rms / max_amplitude, 1.0)

                    # 将音量映射到嘴巴张开度(0-1)
                    # 使用非线性映射增强效果
                    mouth_open = min(volume * 2.5, 1.0)

                    # 添加口型数据
                    timestamp = i / sample_rate
                    lipsync_data.append(
                        LipSyncData(
                            timestamp=timestamp,
                            volume=volume,
                            mouth_open=mouth_open
                        )
                    )

                self.logger.debug(f"提取了 {len(lipsync_data)} 帧口型数据")
                return lipsync_data

        except Exception as e:
            self.logger.error(f"提取口型数据失败: {e}")
            return []

    def play_audio(self, audio: Optional[AudioSegment] = None):
        """
        播放音频
        
        Args:
            audio: 音频对象,如果为None则播放当前音频
        """
        import uuid
        
        try:
            if audio is None:
                audio = self.current_audio

            if audio is None:
                self.logger.warning("没有可播放的音频")
                return

            self.logger.info("正在播放语音...")
            
            # 使用项目目录下的临时文件
            temp_play_file = self.temp_dir / f"play_{uuid.uuid4().hex[:8]}.wav"
            
            try:
                # 导出音频
                audio.export(str(temp_play_file), format="wav")
                self.temp_manager.track_file(temp_play_file)
                
                # 使用 pygame 播放
                import pygame
                pygame.mixer.init()
                pygame.mixer.music.load(str(temp_play_file))
                pygame.mixer.music.play()
                
                # 等待播放完成
                while pygame.mixer.music.get_busy():
                    time.sleep(0.1)
                
                pygame.mixer.quit()
                
            finally:
                # 清理临时文件
                self.temp_manager.cleanup_file(temp_play_file)
            
            self.logger.info("✓ 语音播放完成")

        except Exception as e:
            self.logger.error(f"播放音频失败: {e}")

    def save_audio(self, audio: AudioSegment, output_path: str):
        """
        保存音频到文件
        
        Args:
            audio: 音频对象
            output_path: 输出文件路径
        """
        try:
            audio.export(output_path, format='wav')
            self.logger.info(f"✓ 音频已保存: {output_path}")
        except Exception as e:
            self.logger.error(f"保存音频失败: {e}")
    
    def cleanup_temp_files(self):
        """清理所有临时文件"""
        try:
            if self.temp_dir.exists():
                import glob
                temp_files = glob.glob(str(self.temp_dir / "tts_*.wav"))
                for file_path in temp_files:
                    try:
                        os.unlink(file_path)
                    except Exception as e:
                        self.logger.warning(f"删除临时文件失败 {file_path}: {e}")
                self.logger.debug(f"清理了 {len(temp_files)} 个临时文件")
        except Exception as e:
            self.logger.error(f"清理临时文件失败: {e}")

    def get_audio_duration(self, audio: Optional[AudioSegment] = None) -> float:
        """
        获取音频时长(秒)
        
        Args:
            audio: 音频对象
            
        Returns:
            音频时长(秒)
        """
        if audio is None:
            audio = self.current_audio

        if audio is None:
            return 0.0

        return len(audio) / 1000.0


class AudioPlayer:
    """音频播放器(支持异步播放和口型同步)"""

    def __init__(self):
        """初始化音频播放器"""
        self.is_playing = False
        self.current_lipsync_data: List[LipSyncData] = []
        self.current_frame_index = 0
        self.start_time = 0.0
        self.logger = get_logger()
        
        # 使用项目临时目录而不是 C 盘
        self.temp_manager = get_temp_file_manager()
        self.temp_dir = self.temp_manager.get_temp_dir('audio')

    def play_with_lipsync(
        self,
        audio: AudioSegment,
        lipsync_data: List[LipSyncData],
        on_frame_callback=None
    ):
        """
        播放音频并同步口型
        
        Args:
            audio: 音频对象
            lipsync_data: 口型同步数据
            on_frame_callback: 每帧回调函数(参数为LipSyncData)
        """
        import uuid
        import threading
        
        try:
            self.is_playing = True
            self.current_lipsync_data = lipsync_data
            self.current_frame_index = 0
            self.start_time = time.time()

            self.logger.info("开始播放音频(带口型同步)...")

            # 使用项目目录下的临时文件，而不是 C 盘
            temp_play_file = self.temp_dir / f"play_{uuid.uuid4().hex[:8]}.wav"
            
            try:
                # 导出音频到项目临时目录
                audio.export(str(temp_play_file), format="wav")
                self.temp_manager.track_file(temp_play_file)
                
                # 启动音频播放(异步)
                play_thread = threading.Thread(
                    target=self._play_audio_file, 
                    args=(str(temp_play_file),)
                )
                play_thread.start()

                # 同步口型数据（优化：更精准的时间同步）
                while self.is_playing and self.current_frame_index < len(lipsync_data):
                    elapsed_time = time.time() - self.start_time
                    current_data = lipsync_data[self.current_frame_index]

                    # 如果当前时间已到达该帧时间戳
                    if elapsed_time >= current_data.timestamp:
                        if on_frame_callback:
                            on_frame_callback(current_data)
                        self.current_frame_index += 1
                    else:
                        # 计算到下一帧的等待时间，避免过度轮询
                        time_to_next_frame = current_data.timestamp - elapsed_time
                        # 等待时间不超过1/60秒，保证响应速度
                        sleep_time = min(time_to_next_frame, 1.0 / 120)  # 120fps检查频率更精准
                        if sleep_time > 0:
                            time.sleep(sleep_time)

                # 等待音频播放完成
                play_thread.join()
                
            finally:
                # 清理临时文件
                self.temp_manager.cleanup_file(temp_play_file)

            self.is_playing = False
            self.logger.info("✓ 音频播放完成")

        except Exception as e:
            self.logger.error(f"播放音频失败: {e}")
            self.is_playing = False
    
    def _play_audio_file(self, file_path: str):
        """
        播放音频文件(内部方法)
        支持指定虚拟音频设备，实现本地静音但直播有声
        
        Args:
            file_path: 音频文件路径
        """
        try:
            # 使用 pygame 播放音频，避免 pydub 的 C 盘临时文件问题
            import pygame
            import os
            
            # 检查是否配置了虚拟音频设备
            virtual_device = os.environ.get('VIRTUAL_AUDIO_DEVICE', '')
            
            if virtual_device:
                # 使用指定的虚拟音频设备（本地静音，直播有声）
                try:
                    pygame.mixer.init(devicename=virtual_device)
                    self.logger.info(f"使用虚拟音频设备: {virtual_device}")
                except Exception as e:
                    self.logger.warning(f"虚拟音频设备初始化失败，使用默认设备: {e}")
                    pygame.mixer.init()
            else:
                # 使用默认音频设备
                pygame.mixer.init()
            
            pygame.mixer.music.load(file_path)
            pygame.mixer.music.play()
            
            # 等待播放完成
            while pygame.mixer.music.get_busy():
                time.sleep(0.1)
            
            pygame.mixer.quit()
            
        except Exception as e:
            self.logger.error(f"播放音频文件失败: {e}")
            # 如果 pygame 失败，尝试使用系统播放器
            try:
                import subprocess
                import platform
                
                system = platform.system()
                if system == "Windows":
                    # Windows 使用 start 命令
                    subprocess.run(["start", "", file_path], shell=True, check=False)
                    # 等待一下（简单估计）
                    time.sleep(3)
                elif system == "Darwin":  # macOS
                    subprocess.run(["afplay", file_path], check=False)
                else:  # Linux
                    subprocess.run(["aplay", file_path], check=False)
            except Exception as e2:
                self.logger.error(f"备用播放方法也失败: {e2}")

    def stop(self):
        """停止播放"""
        self.is_playing = False
